use crate::{Rectangle, Vector};

use super::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct LineSegment {
	pub start: Point,
	pub end: Point,
}
impl LineSegment {
	pub fn new(start: Point, end: Point) -> Self {
		Self { start, end }
	}
	pub fn start(self) -> Point {
		self.start
	}
	pub fn end(self) -> Point {
		self.end
	}
	/// Compares `self` to the point `p`.
	///
	/// Returns `Ordering::Less` if `self` lies below `p`, `Ordering::Greater` if `self` lies
	/// above `p`, and `Ordering::Equal` if `self` is incident to `p`.
	pub fn compare_to_point(self, point: Point) -> Option<core::cmp::Ordering> {
		// Compute the signed area of the triangle with vertices `p`, `p0`, and `p1`.
		(point - self.start).cross(self.end - point).partial_cmp(&0.0)
	}
	pub fn intersect(self, other: Self) -> Option<Point> {
		let a = self.end - self.start;
		let b = other.start - other.end;
		let c = self.start - other.start;
		let denom = a.cross(b);
		if denom == 0.0 {
			return None;
		}
		let numer_0 = b.cross(c);
		let numer_1 = c.cross(a);
		if denom < 0.0 {
			if (numer_0 < denom || 0.0 < numer_0) || (numer_1 < denom || 0.0 < numer_1) {
				return None;
			}
		} else {
			if (numer_0 < 0.0 || denom < numer_0) || (numer_1 < 0.0 || denom < numer_1) {
				return None;
			}
		}
		Some(self.start.lerp(self.end, numer_0 / denom))
	}
	/// Returns the intersection point of the supporting line of `self` with the vertical line
	/// through `x`, or None if these lines are coincident.
	pub fn intersect_with_vertical_line(self, x: f32) -> Option<Point> {
		fn ext_lerp(x: f32, other: f32, t: f32) -> f32 {
			x * (1.0 - t) + other * t
		}
		let dx = self.end.x() - self.start.x();
		if dx == 0.0 {
			return None;
		}
		let dx1 = x - self.start.x();
		let dx2 = self.end.x() - x;
		Some(Point::new(
			x,
			if dx1 <= dx2 {
				ext_lerp(self.start.y(), self.end.y(), dx1 / dx)
			} else {
				ext_lerp(self.end.y(), self.start.y(), dx2 / dx)
			},
		))
	}
	pub fn normal(self) -> Vector {
		self.tangent().perpendicular()
	}
	pub fn tangent(self) -> Vector {
		self.end - self.start
	}
	pub fn bounds(self) -> Rectangle {
		Rectangle::new(
			Point::new(self.start.x().min(self.end.x()), self.start.y().min(self.end.y())),
			Point::new(self.start.x().max(self.end.x()), self.start.y().max(self.end.y())),
		)
	}
	pub fn reverse(self) -> Self {
		Self {
			end: self.start,
			start: self.end,
		}
	}
}

impl Transform for LineSegment {
	fn transform(self, t: &impl Transformation) -> Self	{
		LineSegment::new(self.start.transform(t), self.end.transform(t))
	}
	fn transform_mut(&mut self, t: &impl Transformation)	{
		*self = self.transform(t);
	}
}